#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "wave.h"
#include "Huffman.h"

Wave* wave_load(const char* filename)
{
	FILE* fd;
	int tint;
	short tshort;
	Wave *w;
	char tinfo[4];
	int samples;
	int hsize, nbytes; // header size, total size

	if ((fd = fopen(filename, "rb")) == 0)
		return 0;
	if ((w = (Wave*)malloc(sizeof(Wave))) == 0)
	{
		fclose(fd);
		return 0;
	}

	/* parsing header */
	if (fread(tinfo, 1, 4, fd) != 4)
		goto WRONG;
	else if (strncmp(tinfo, "RIFF", 4) != 0)
		goto WRONG;
	fread(&tint, 4, 1, fd);
	nbytes = tint; //
	if ((fread(tinfo, 1, 4, fd) != 4) || (strncmp(tinfo, "WAVE", 4) != 0))
		goto WRONG;
	if ((fread(tinfo, 1, 4, fd) != 4) || (strncmp(tinfo, "fmt ", 4) != 0))
		goto WRONG;
	fread(&tint, 4, 1, fd);
	hsize = tint; //
	fread(&tshort, 2, 1, fd);
	if (tshort != 0x0001) // riff's pcm
		goto WRONG;
	fread(&tshort, 2, 1, fd);
	w->nChannels = tshort; //
	fread(&tint, 4, 1, fd);
	w->samplerates = tint; //
	fread(&tint, 4, 1, fd); // byte rate
	fread(&tshort, 2, 1, fd); // block align
	fread(&tshort, 2, 1, fd); // bps
	w->bps = tshort;
	fseek(fd, ftell(fd) + (hsize - 16), SEEK_SET); // skip the rest of header

	/* parsing data header */
	while (1)
	{
		if (fread(&tinfo, 1, 4, fd) != 4)
			goto WRONG;
		if (strncmp(tinfo, "data", 4) == 0)
		{
			fread(&tint, 4, 1, fd);
			samples = tint / sizeof(BYTE);
			break;
		}
		else if (strncmp(tinfo, "fact", 4) == 0)
		{
			fread(&tint, 4, 1, fd);
			fseek(fd, ftell(fd) + tint, SEEK_SET);
		}
		else if (strncmp(tinfo, "clm ", 4) == 0)
		{
			fread (&tint, 4, 1, fd);
			fseek(fd, ftell(fd) + tint, SEEK_SET);
		}
		else
			goto WRONG;
	}

	/* parsing data */
	w->samples = (BYTE*)malloc(samples * w->bps / 8 * sizeof(BYTE)); //
	w->nSamples = samples / w->nChannels; //
	if ((tint = fread(w->samples, w->bps / 8, samples, fd)) != samples)
		w->nSamples = tint;

	w->encoded = FALSE;

	fclose(fd);
	return w;

WRONG:
	free(w);
	fclose(fd);
	return 0;
}

int wave_save(const char* filename, Wave *w)
{
	FILE* fd;
	int tint;
	short tshort;

	if ((fd = fopen(filename, "wb")) == 0)
		return -1;
	// chunk head - 8 bytes
	fwrite("RIFF", 1, 4, fd);
	tint = w->nChannels * w->nSamples * (w->bps / 8) + 12 + 8 + 16;
	fwrite(&tint, 4, 1, fd);
	fwrite("WAVE", 1, 4, fd); // ~ 12 bytes
	// sub chunk1 header - 8 bytes
	fwrite("fmt ", 1, 4, fd); 
	tint = 16; // sub chunk1 header size
	fwrite(&tint, 4, 1, fd);
	// sub chunk1 data - 16 bytes
  tshort = 0x0001;
	fwrite(&tshort, 2, 1, fd);
	fwrite(&w->nChannels, 2, 1, fd);
	fwrite(&w->samplerates, 4, 1, fd); // sample rate
	tint = w->samplerates * w->nChannels * (w->bps / 8); // byte rate
	fwrite(&tint, 4, 1, fd);
	tshort = w->nChannels * (w->bps / 8); // block align
	fwrite(&tshort, 2, 1, fd);
	tshort = w->bps;	// bps
	fwrite(&tshort, 2, 1, fd); // ~ 8 bytes
  
  fwrite("data", 1, 4, fd);
	tint = w->nChannels * w->nSamples * (w->bps / 8);
	fwrite(&tint, 4, 1, fd); // data length

	if(fwrite(w->samples, (w->bps / 8), w->nChannels * w->nSamples, fd) 
		!= w->nChannels * w->nSamples)
		goto WRONG;
	
	fclose(fd);
	return 0;

WRONG:
	fclose(fd);
	return -1;
}

Wave* wave_synth(Wave* w1, float pfw1, float pew1, Wave* w2, float pfw2, float pew2)
{
	Wave* w3;
	int flen1, elen1, flen2, elen2;
	if ((w3 = (Wave*)malloc(sizeof(Wave))) == 0)
		return 0;

	if (w2 == 0)
	{
		w3->nChannels = w1->nChannels;
		flen1 = pfw1 * w1->nSamples;
		elen1 = pew1 * w1->nSamples;
		w3->nSamples = elen1 - flen1;
		w3->samplerates = w1->samplerates;
		w3->bps = w1->bps;
		if ((w3->samples = (BYTE*)malloc(w3->nSamples * w3->nChannels * w3->bps / 8 * sizeof(BYTE))) == 0)
			goto WRONG;
		flen1 *= w3->nChannels;
		memmove(w3->samples, &w1->samples[flen1], w3->nSamples * w3->nChannels * w3->bps / 8);
	}
	else 
	{
		if ((w1->nChannels != w2->nChannels) || (w1->samplerates != w2->samplerates) || (w1->bps != w2->bps))
			goto WRONG;
	
		w3->nChannels = w1->nChannels;
		flen1 = pfw1 * w1->nSamples;
		elen1 = pew1 * w1->nSamples;
		flen2 = pfw2 * w2->nSamples;
		elen2 = pew2 * w2->nSamples;
		w3->nSamples = elen1 + elen2 - flen1 - flen2;
		w3->samplerates = w1->samplerates;
		w3->bps = w1->bps;
		if ((w3->samples = (BYTE*)malloc(w3->nSamples * w3->nChannels * w3->bps / 8 * sizeof(BYTE))) == 0)
			goto WRONG;
		flen1 *= w1->nChannels;
		elen1 *= w1->nChannels;
		flen2 *= w2->nChannels;
		elen2 *= w2->nChannels;
		memmove(w3->samples, &w1->samples[flen1] , (elen1 - flen1) * w3->bps / 8);
		memmove(&w3->samples[elen1 - flen1], &w2->samples[flen2], (elen2 - flen2) * w3->bps / 8);
	}

	return w3;

WRONG:
	free(w3);
	return 0;
}

Wave* wave_load_huff(const char* filename)
{
	FILE* fd;
	Wave *w;
	char tinfo[4];
	int tint;
	Wave twav;

	if ((fd = fopen(filename, "rb")) == 0)
		return 0;
	if ((w = (Wave*)malloc(sizeof(Wave))) == 0)
	{
		fclose(fd);
		return 0;
	}

	/* parsing header */
	if (fread(tinfo, 1, 4, fd) != 4)
		goto WRONG;
	else if (strncmp(tinfo, "HUFF", 4) != 0)
		goto WRONG;
	fread(&twav, sizeof(Wave) - 4, 1, fd);
	w->bps = twav.bps;
	w->encoded = TRUE;
	w->nChannels = twav.nChannels;
	w->nSamples = twav.nSamples;
	w->samplerates = twav.samplerates;
	w->sizeEncoded = twav.sizeEncoded;
	if ((w->samples = (BYTE*)malloc(w->sizeEncoded * sizeof(BYTE))) == 0)
		goto WRONG;
	/* parse data */
	if ((tint = fread(w->samples, sizeof(BYTE), w->sizeEncoded, fd)) != w->sizeEncoded)
	{
		free (w->samples);
		goto WRONG;
	}

	fclose(fd);
	return w;

WRONG:
	free(w);
	fclose(fd);
	return 0;
}

int wave_save_huff(const char* filename, Wave *w)
{
	FILE* fd;
	Wave twav;
	int tint;

	if ((fd = fopen(filename, "wb")) == 0)
		return -1;

	/* saving header */
	fwrite("HUFF", 1, 4, fd);
	twav.bps = w->bps;
	twav.encoded = TRUE;
	twav.nChannels = w->nChannels;
	twav.nSamples = w->nSamples;
	twav.samplerates = w->samplerates;
	twav.sizeEncoded = w->sizeEncoded;
	fwrite(&twav, sizeof(Wave) - 4, 1, fd);
	
	/* saving data */
	if ((tint = fwrite(w->samples, sizeof(BYTE), w->sizeEncoded, fd)) == w->sizeEncoded)
	{
		free (w->samples);
		goto WRONG;
	}

	fclose(fd);
	return 0;

WRONG:
	fclose(fd);
	return -1;
}

Wave* wave_encode_huffman(Wave* w, BOOL isDiff)
{
	Wave *wo;
	int dlen;

	if ((wo = (Wave*)malloc(sizeof(Wave))) == 0)
		return 0;

	if (isDiff == FALSE && CompressHuffman(w->samples, w->nChannels * w->nSamples * w->bps / 8, &wo->samples, &dlen) == FALSE)
	{
		free (wo);
		return 0;
	}
	else if (isDiff == TRUE && CompressDifferentialHuffman(w->samples, w->nChannels * w->nSamples * w->bps / 8, &wo->samples, &dlen) == FALSE)
	{
		free (wo);
		return 0;
	}
	wo->bps = w->bps;
	wo->encoded = TRUE;
	wo->nChannels = w->nChannels;
	wo->nSamples = w->nSamples;
	wo->samplerates = w->samplerates;
	wo->sizeEncoded = dlen;

	return wo;
}

Wave* wave_decode_huffman(Wave* w, BOOL isDiff)
{
	Wave *wo;
	int dlen;

	if ((wo = (Wave*)malloc(sizeof(Wave))) == 0)
		return 0;
	if (isDiff == FALSE && DecompressHuffman(w->samples, w->sizeEncoded, &wo->samples, &dlen) == FALSE)
	{
		free (wo);
		return 0;
	}
	else if (isDiff == TRUE && DecompressDifferentialHuffman(w->samples, w->sizeEncoded, &wo->samples, &dlen) == FALSE)
	{
		free (wo);
		return 0;
	}
	wo->bps = w->bps;
	wo->encoded = FALSE;
	wo->nChannels = w->nChannels;
	wo->nSamples = w->nSamples;
	wo->samplerates = w->samplerates;
	wo->sizeEncoded = -1;

	return wo;
}

void wave_free(Wave *w)
{
	if (w != 0)
	{
		free(w->samples);
		free(w);
	}
}

Wave* wave_load_leasthuff(const char* filename)
{
	FILE* fd;
	Wave *w;
	WaveEntry entry;
	int i;

	if ((fd = fopen(filename, "rb")) == 0)
		return 0;
	if ((w = (Wave*)malloc(sizeof(Wave))) == 0)
	{
		fclose(fd);
		return 0;
	}
	// read Header
	if (fread(w, sizeof(Wave), 1, fd) != sizeof(Wave) || w->encoded == FALSE) goto WRONG;
	// read Entries
	w->entries = (WaveEntry**)malloc(sizeof(WaveEntry*)*w->nWaves);
	for(i = 0; i < w->nWaves; i++)
	{
		fread(&entry, sizeof(WaveEntry), 1, fd);
		w->entries[i] = (WaveEntry*)malloc(sizeof(WaveEntry));
		w->entries[i]->index = entry.index;
		w->entries[i]->length = entry.length;
	}
	// read data
	w->samples = (BYTE*)malloc(sizeof(BYTE) * w->sizeEncoded);
	if (fread(w->samples, sizeof(BYTE), w->sizeEncoded, fd) != sizeof(BYTE) * w->sizeEncoded) goto WRONG2;

	fclose(fd);
	return w;	

WRONG2:
	free(w->samples);
WRONG:
	fclose(fd);
	free(w);
	return 0;
}

int wave_save_leasthuff(const char* filename, Wave *w)
{
	FILE* fd;
	WaveEntry entry;
	int i, tmp;

	if ((fd = fopen(filename, "wb")) == 0) return 0;
	// write Header
	if (fwrite(w, sizeof(Wave), 1, fd) != sizeof(Wave)) return 0;
	// write Entries
	for(i = 0; i < w->nWaves; i++)
	{
		entry.index = w->entries[i]->index;
		entry.length = w->entries[i]->length;
		if (fwrite(&entry, sizeof(WaveEntry), 1, fd) != sizeof(WaveEntry)) return 0;
	}
	// write data
	if ((tmp = fwrite(w->samples, sizeof(BYTE) * w->sizeEncoded, 1, fd)) 
		!= w->sizeEncoded * sizeof(BYTE)) return 0;

	return tmp;
}